#include "lib/drivers/pvmdriver_softmix.h"
#include "lib/drivers/pvmdeviceinterface.h"
#include "lib/pvmerrors.h"
#include "lib/softwaremixer/pvmsoftmixer.h"
#include "lib/softwaremixer/pvmsoftmixerfastint.h"
#include "lib/3d/pvm3dinterface.h"
//#include "lib/3d/pvm3dsoftware_fixed.h"
#include "lib/3d/pvm3dsoftware_float.h"

#include "lib/memory/pvmmemory.h"

class PVMDeviceSimple : public IPVMDeviceOut, IPVMDeviceOutSimpleWaveSource, public PVMAllocableObject
{
public:
	PVMDeviceSimple(PVMDriverSimple* driver);
	~PVMDeviceSimple();

	virtual pvm_uint32 GetDeviceType();
	virtual void DeviceClose();
	virtual pvm_bool DeviceOpened();

	virtual void Suspend();
	virtual pvm_bool IsSuspended();
	virtual void Resume();

	virtual void DeviceNotify();

	virtual void SetBufferLengthMs(pvm_uint32 bufferSize);
	virtual pvm_uint32 GetBufferRealLengthMs();

	virtual pvm_int32 GetCPUUsage();

	virtual pvm_uint32 GetChanelsNum();
	virtual pvm_uint32 GetBitsPerSample();
	virtual pvm_uint32 GetSamplesPerSecond();

	virtual void SetMixingVolume(pvm_uint32 volume);
	virtual pvm_uint32 GetMixingVolume();

	virtual pvm_int32 PlayVoice(IPVMVoice* voice, pvm_int32 voiceNumAndFlags, TVoiceCallbackProc callbackProc, pvm_uint32 callbackPeriod, void* calbackParam);
	virtual void StopVoice(pvm_uint32 voiceSlot);
	virtual pvm_bool IsVoiceFree(pvm_uint32 voiceSlot);

	virtual pvm_bool SetRate(pvm_uint32 voiceSlot, pvm_int32 value);
	virtual pvm_bool SetPanning(pvm_uint32 voiceSlot, pvm_int32 value);
	virtual pvm_bool SetVolume(pvm_uint32 voiceSlot, pvm_int32 value);
	virtual pvm_bool SetLoopCount(pvm_uint32 voiceSlot, pvm_int32 value);
	virtual pvm_bool SetLoopInSamples(pvm_uint32 voiceSlot, pvm_int64 loopFrom, pvm_int64 loopTo);
	virtual pvm_bool SetPause(pvm_uint32 voiceSlot, pvm_bool value);
	virtual pvm_bool SetRenderSilentVoice(pvm_uint32 voiceSlot, pvm_bool value);

	virtual pvm_bool GetRate(pvm_uint32 voiceSlot, pvm_int32& value);
	virtual pvm_bool GetPanning(pvm_uint32 voiceSlot, pvm_int32& value);
	virtual pvm_bool GetVolume(pvm_uint32 voiceSlot, pvm_int32& value);
	virtual pvm_bool GetLoopCount(pvm_uint32 voiceSlot, pvm_int32& value);
	virtual pvm_bool GetLoopInSamples(pvm_uint32 voiceSlot, pvm_int64& loopFrom, pvm_int64& loopTo);
	virtual pvm_bool GetLoopInMS(pvm_uint32 voiceSlot, pvm_int32& loopFrom, pvm_int32& loopTo);
	virtual pvm_bool GetPause(pvm_uint32 voiceSlot, pvm_bool& value);
	virtual pvm_bool GetRenderSilentVoice(pvm_uint32 voiceSlot, pvm_bool& value);

	virtual pvm_bool SetPositionMS(pvm_uint32 voiceSlot, pvm_int32 value);
	virtual pvm_bool GetPositionMS(pvm_uint32 voiceSlot, pvm_int32& value);

	virtual pvm_bool SetVoiceOption(pvm_uint32 voiceSlot, const pvm_char* optionName, void* param, pvm_uint32 paramSize);
	virtual pvm_bool GetVoiceOption(pvm_uint32 voiceSlot, const pvm_char* optionName, void* param, pvm_uint32* paramSize);

	virtual pvm_uint32 GetMaxVoices();

	virtual pvm_bool BeginBatchParams();
	virtual void EndBatchParams();

	virtual pvm_bool SetPanningVolumeRate(pvm_uint32 chanelNum, pvm_int32 panning, pvm_int32 volume, pvm_int32 rate);

	virtual pvm_uint32 Get3dSupportFlags();
	virtual pvm_bool Choose3dInterface(pvm_uint32 realizationType);
	virtual IPVM3DInterface* Get3dInterface();

	virtual pvm_bool SetOption(const pvm_char* optionName, void* param, pvm_uint32 paramSize);
	virtual pvm_bool GetOption(const pvm_char* optionName, void* param, pvm_uint32* paramSize);

	bool Init(IPVMDeviceOutSimple* device, unsigned int maxVoices, unsigned int sampleRate, unsigned int bitsPerSample, unsigned int chanelsNum);

	virtual pvm_bool IsInitialized();
	virtual void ResizeRenderBuffer(pvm_uint32 bufferSizeInBytes);
	virtual pvm_uint32 GetRenderBufferSizeInBytes();
	virtual pvm_bool HasMoreData();
	virtual pvm_uint32 FillBuffer(void * buffer, pvm_uint32 bufferSizeInSamples);

	virtual IPVMDriverBase* GetDriver();
	virtual void OnIdle();
	virtual void SetForceProcessingOnIdle(pvm_bool flag);

	void SetNextDevice(PVMDeviceSimple* nextDevice) {this->nextDevice = nextDevice;}
	PVMDeviceSimple* GetNextDevice() { return nextDevice;}
protected:
	void CloseSimpleOutDevice();
protected:
	//rendering sources
	PVMCriticalSection pvmdRenderGuard;

	IPVMSoftMixer* mixer;
	IPVMDeviceOutSimple* simpleOutDevice;
	IPVM3DInterface* i3d;
	PVMDriverSimple* driver;
	pvm_bool forceProcessOnIdle;

	PVMDeviceSimple* nextDevice;
};

pvm_uint32 PVMDeviceSimple::GetChanelsNum()
{
	if(!simpleOutDevice)
		return 0;

	return simpleOutDevice->GetChanelsNum();
}

pvm_uint32 PVMDeviceSimple::GetBitsPerSample()
{
	if(!simpleOutDevice)
		return 0;

	return simpleOutDevice->GetBitsPerSample();
}

pvm_uint32 PVMDeviceSimple::GetSamplesPerSecond()
{
	if(!simpleOutDevice)
		return 0;

	return simpleOutDevice->GetSamplesPerSecond();
}

void PVMDeviceSimple::Suspend()
{
	if(simpleOutDevice)
		simpleOutDevice->Suspend();
}

pvm_bool PVMDeviceSimple::IsSuspended()
{
	if(simpleOutDevice)
		return simpleOutDevice->IsSuspended();

	return false;
}

void PVMDeviceSimple::Resume()
{
	if(simpleOutDevice)
		simpleOutDevice->Resume();
}

void PVMDeviceSimple::DeviceNotify()
{
	if(simpleOutDevice)
		simpleOutDevice->DeviceNotify();
}

void PVMDeviceSimple::SetBufferLengthMs(pvm_uint32 bufferSize)
{
	if(simpleOutDevice)
		simpleOutDevice->SetBufferLengthMs(bufferSize);
}

pvm_uint32 PVMDeviceSimple::GetBufferRealLengthMs()
{
	if(simpleOutDevice)
		return simpleOutDevice->GetBufferRealLengthMs();

	return 0;
}

pvm_int32 PVMDeviceSimple::GetCPUUsage()
{
	if(simpleOutDevice)
		return simpleOutDevice->GetCPUUsage();

	return 0;
}

PVMDeviceSimple::PVMDeviceSimple(PVMDriverSimple* driver)
{
	nextDevice = NULL;
	simpleOutDevice = NULL;
	i3d = NULL;
	mixer = NULL;
	this->driver = driver;
	forceProcessOnIdle = false;
}

PVMDeviceSimple::~PVMDeviceSimple()
{
	CloseSimpleOutDevice();

	delete( i3d); i3d = NULL;

	simpleOutDevice = NULL;

	if(mixer!=NULL)
	{
		mixer->DeInit();
	}
	delete( mixer); mixer = NULL;

	driver = NULL;
}

void PVMDeviceSimple::CloseSimpleOutDevice()
{
	if(simpleOutDevice)
	{
		IPVMDriverBase* simpleDriver = simpleOutDevice->GetDriver();
		if(simpleDriver!=NULL)
			simpleDriver->CloseDevice(simpleOutDevice);
		else
			simpleOutDevice->DeviceClose();

		simpleOutDevice = NULL;
	}
}

bool PVMDeviceSimple::Init(IPVMDeviceOutSimple* device, unsigned int maxVoices, unsigned int sampleRate, unsigned int bitsPerSample, unsigned int chanelsNum)
{
	if(!device)
		return false;

	CloseSimpleOutDevice();

	mixer = new PVMSoftMixerFastInt();
	if(mixer==NULL)
		return false;

	if(mixer->Init(maxVoices, sampleRate, bitsPerSample, chanelsNum)==false)
	{
		delete( mixer);
		mixer = NULL;
		return false;
	}

	mixer->SetForceProcessingOnIdle(forceProcessOnIdle);

	simpleOutDevice = device;

	return true;
}

pvm_uint32 PVMDeviceSimple::GetDeviceType()
{
	if(simpleOutDevice)
		return simpleOutDevice->GetDeviceType()|EPVM_DTYPE_OUTPUT;

	return 0;
}

void PVMDeviceSimple::DeviceClose()
{
	delete( this);
}

pvm_bool PVMDeviceSimple::DeviceOpened()
{
	if(simpleOutDevice)
		return simpleOutDevice->DeviceOpened();
	return false;
}

void PVMDeviceSimple::SetMixingVolume(pvm_uint32 volume)
{
	mixer->SetGlobalVolume(volume);
}

pvm_uint32 PVMDeviceSimple::GetMixingVolume()
{
	return mixer->GetGlobalVolume();
}

pvm_int32 PVMDeviceSimple::PlayVoice(IPVMVoice* voice, pvm_int32 voiceNumAndFlags, TVoiceCallbackProc callbackProc, pvm_uint32 callbackPeriod, void* calbackParam)
{
	pvm_int32 playRes = mixer->Play(voice, voiceNumAndFlags, callbackProc, callbackPeriod, calbackParam);
	if(playRes!=-1)
		DeviceNotify();
	return playRes;
}

void PVMDeviceSimple::StopVoice(pvm_uint32 voiceSlot)
{
	mixer->Stop(voiceSlot);
}

pvm_bool PVMDeviceSimple::IsVoiceFree(pvm_uint32 voiceSlot)
{
	return mixer->IsVoiceFree(voiceSlot);
}

pvm_bool PVMDeviceSimple::SetRate(pvm_uint32 voiceSlot, pvm_int32 value)
{
	return mixer->SetRate(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::SetPanning(pvm_uint32 voiceSlot, pvm_int32 value)
{
	return mixer->SetPanning(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::SetVolume(pvm_uint32 voiceSlot, pvm_int32 value)
{
	return mixer->SetVolume(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::SetLoopCount(pvm_uint32 voiceSlot, pvm_int32 value)
{
	return mixer->SetLoopCount(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::SetLoopInSamples(pvm_uint32 voiceSlot, pvm_int64 loopFrom, pvm_int64 loopTo)
{
	return mixer->SetLoopInSamples(voiceSlot, loopFrom, loopTo);
}

pvm_bool PVMDeviceSimple::SetPause(pvm_uint32 voiceSlot, pvm_bool value)
{
	pvm_bool retVal = mixer->SetPause(voiceSlot, value);
	if(!value)
		DeviceNotify();
	return retVal;
}

pvm_bool PVMDeviceSimple::SetRenderSilentVoice(pvm_uint32 voiceSlot, pvm_bool value)
{
	pvm_bool retVal = mixer->SetRenderSilentVoice(voiceSlot, value);
	if(!value)
		DeviceNotify();
	return retVal;
}

pvm_bool PVMDeviceSimple::GetRate(pvm_uint32 voiceSlot, pvm_int32& value)
{
	return mixer->GetRate(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::GetPanning(pvm_uint32 voiceSlot, pvm_int32& value)
{
	return mixer->GetPanning(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::GetVolume(pvm_uint32 voiceSlot, pvm_int32& value)
{
	return mixer->GetVolume(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::GetLoopCount(pvm_uint32 voiceSlot, pvm_int32& value)
{
	return mixer->GetLoopCount(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::GetLoopInSamples(pvm_uint32 voiceSlot, pvm_int64& loopFrom, pvm_int64& loopTo)
{
	return mixer->GetLoopInSamples(voiceSlot, loopFrom, loopTo);
}

pvm_bool PVMDeviceSimple::GetLoopInMS(pvm_uint32 voiceSlot, pvm_int32& loopFrom, pvm_int32& loopTo)
{
	return mixer->GetLoopInMS(voiceSlot, loopFrom, loopTo);
}

pvm_bool PVMDeviceSimple::GetPause(pvm_uint32 voiceSlot, pvm_bool& value)
{
	return mixer->GetPause(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::GetRenderSilentVoice(pvm_uint32 voiceSlot, pvm_bool& value)
{
	return mixer->GetRenderSilentVoice(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::SetPositionMS(pvm_uint32 voiceSlot, pvm_int32 value)
{
	return mixer->SetPositionMS(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::GetPositionMS(pvm_uint32 voiceSlot, pvm_int32& value)
{
	return mixer->GetPositionMS(voiceSlot, value);
}

pvm_bool PVMDeviceSimple::SetVoiceOption(pvm_uint32 voiceSlot, const pvm_char* optionName, void* param, pvm_uint32 paramSize)
{
	return mixer->SetVoiceOption(voiceSlot, optionName, param, paramSize);
}

pvm_bool PVMDeviceSimple::GetVoiceOption(pvm_uint32 voiceSlot, const pvm_char* optionName, void* param, pvm_uint32* paramSize)
{
	return mixer->GetVoiceOption(voiceSlot, optionName, param, paramSize);
}

pvm_uint32 PVMDeviceSimple::GetMaxVoices()
{
	return mixer->GetMaxVoices();
}

pvm_bool PVMDeviceSimple::BeginBatchParams()
{
	return mixer->BeginBatchParams();
}

void PVMDeviceSimple::EndBatchParams()
{
	mixer->EndBatchParams();
	DeviceNotify();
}

pvm_bool PVMDeviceSimple::SetPanningVolumeRate(pvm_uint32 chanelNum, pvm_int32 panning, pvm_int32 volume, pvm_int32 rate)
{
	return mixer->SetPanningVolumeRateFast(chanelNum, panning, volume, rate);
}

pvm_uint32 PVMDeviceSimple::Get3dSupportFlags()
{
	return E_3D_SOFTWARE_FLOAT|E_3D_SOFTWARE_FIXED;
}

pvm_bool PVMDeviceSimple::Choose3dInterface(pvm_uint32 realizationType)
{
	if(i3d!=NULL)
	{
		delete( i3d);
		i3d = NULL;
	}

	if(realizationType==E_3D_SOFTWARE_FLOAT)
	{
		i3d = new PVM3DSoftwareFloatClass(this);
	}
#if !defined(IPHONE) && 0
	else if(realizationType==E_3D_SOFTWARE_FIXED)
	{
		i3d = new PVM3DSoftwareFixedClass(this);
	}
#endif
	else if(realizationType==(pvm_uint32)E_3D_OPTIMAL)
	{
		i3d = new PVM3DSoftwareFloatClass(this);
	}

	return i3d!=NULL;
}

IPVM3DInterface* PVMDeviceSimple::Get3dInterface()
{
	return i3d;
}

pvm_bool PVMDeviceSimple::SetOption(const pvm_char* optionName, void* param, pvm_uint32 paramSize)
{
	return mixer->SetOption(optionName, param, paramSize);
}

pvm_bool PVMDeviceSimple::GetOption(const pvm_char* optionName, void* param, pvm_uint32* paramSize)
{
	return mixer->GetOption(optionName, param, paramSize);
}

////////////////////////////////////////////////////////////////////////////////

pvm_bool PVMDeviceSimple::IsInitialized()
{

	if(mixer==NULL)
		return false;

	return mixer->IsInitialized() && simpleOutDevice!=NULL;
}

void PVMDeviceSimple::ResizeRenderBuffer(pvm_uint32 bufferSizeInBytes)
{
	mixer->ResizeRenderBuffer(bufferSizeInBytes);
}

pvm_uint32 PVMDeviceSimple::GetRenderBufferSizeInBytes()
{
	if(mixer==NULL)
		return 0;
	return mixer->GetRenderBufferSizeInBytes();
}

pvm_bool PVMDeviceSimple::HasMoreData()
{
	if(mixer==NULL)
		return false;
	return mixer->HasMoreData();
}

pvm_uint32 PVMDeviceSimple::FillBuffer(void* buffer, pvm_uint32 bufferSizeInSamples)
{
	pvm_uint32 resValue;

#ifdef PVM_USE_MMGR
	//unsigned long prevThreadId = mmgrGetLoggingThreadId();
	//mmgrSetLoggingThreadId(GetCurrentThreadId());
	//mmgrEnableLogAllocations(true);
	//mmgrEnableLogDeallocations(true);
#endif
	resValue = mixer->FillBuffer(buffer, bufferSizeInSamples);
#ifdef PVM_USE_MMGR
	//mmgrSetLoggingThreadId(prevThreadId);
	//mmgrEnableLogAllocations(false);
	//mmgrEnableLogDeallocations(false);
#endif
	return resValue;
}

IPVMDriverBase* PVMDeviceSimple::GetDriver()
{
	return driver;
}

void PVMDeviceSimple::OnIdle()
{
	if(mixer!=NULL && forceProcessOnIdle)
	{
		mixer->OnIdle();
	}
}

void PVMDeviceSimple::SetForceProcessingOnIdle(pvm_bool flag)
{
	forceProcessOnIdle = flag;
	if(mixer!=NULL)
	{
		mixer->SetForceProcessingOnIdle(flag);
	}
}

////////////////////////////////////////////////////////////////////////////////
//PVMDriverSimple															  //
////////////////////////////////////////////////////////////////////////////////

PVMDriverSimple::PVMDriverSimple(IPVMDriverOutSimpleDevice *simpleDriver)
{
	isDriverInitialized = false;
	this->simpleDriver = simpleDriver;
	rootDevice = NULL;
}

PVMDriverSimple::~PVMDriverSimple()
{
	if(simpleDriver)
		simpleDriver->DriverClose();
	simpleDriver = NULL;
}

pvm_uint32 PVMDriverSimple::DeviceHardwareChanels(pvm_int32 /*deviceNume*/)
{
	return 0;
}

pvm_bool PVMDriverSimple::Init()
{
	if(IsInitialized())
	{
		return true;
	}

	isDriverInitialized = true;

	return true;
}

void PVMDriverSimple::DriverClose()
{
	if(IsInitialized())
	{
		isDriverInitialized = false;
	}

	delete( this);
}

pvm_bool PVMDriverSimple::IsInitialized()
{
	return isDriverInitialized;
}

pvm_uint32 PVMDriverSimple::GetDriverType()
{
	return EPVM_DTYPE_OUTPUT;
}

pvm_uint32 PVMDriverSimple::DevicesNum()
{
	if(simpleDriver)
		return simpleDriver->DevicesNum();
	return 0;
}

pvm_int32 PVMDriverSimple::DeviceName(pvm_int32 nDevice, pvm_char* buffer, pvm_uint32 maxLength)
{
	if(simpleDriver)
		return simpleDriver->DeviceName(nDevice, buffer, maxLength);
	return -1;
}

pvm_bool PVMDriverSimple::DeviceEnumModes(pvm_int32 nDeivce, TFormatEnumCallbackProc enumProc, void *param)
{
	if(simpleDriver)
		return simpleDriver->DeviceEnumModes(nDeivce, enumProc, param);
	return false;
}

IPVMDeviceOut* PVMDriverSimple::DeviceOpen(pvm_uint32 maxVoices, pvm_uint32 samplesPerSec, pvm_uint32 bitsPerSample, pvm_uint32 chanelsNum, pvm_int32 deviceNum, void *additionalInfo, pvm_uint32 infoSize)
{
	if(!IsInitialized())
		return NULL;

	if(simpleDriver==NULL)
		return NULL;

	PVMDeviceSimple* device = new PVMDeviceSimple(this);

	IPVMDeviceOutSimple* simpleOuDevice = simpleDriver->DeviceOpen(samplesPerSec, bitsPerSample, chanelsNum, deviceNum, additionalInfo, infoSize, (IPVMDeviceOutSimpleWaveSource*)device, true);
	if(!simpleOuDevice)
	{
		device->DeviceClose();
		return NULL;
	}

	pvm_uint32 actualSamplesPerSec = simpleOuDevice->GetSamplesPerSecond();
	pvm_uint32 actualBitsPerSample = simpleOuDevice->GetBitsPerSample();
	pvm_uint32 actualChanelsNum = simpleOuDevice->GetChanelsNum();

	if(!device->Init(simpleOuDevice, maxVoices, actualSamplesPerSec, actualBitsPerSample, actualChanelsNum))
	{
		device->DeviceClose();
		simpleOuDevice->DeviceClose();
		return NULL;
	}

	device->SetNextDevice(rootDevice);
	rootDevice = device;

	simpleOuDevice->Resume();

	return device;
}

void PVMDriverSimple::CloseDevice(IPVMDeviceBase* device)
{
	if(device==NULL)
		return;

	PVMDeviceSimple* simpleDevice = (PVMDeviceSimple*)device;

	if(rootDevice!=NULL)
	{
		if(simpleDevice==rootDevice)
		{
			rootDevice = simpleDevice->GetNextDevice();
		}
		else
		{
			PVMDeviceSimple* nextDevicePtr = rootDevice;
			while(nextDevicePtr!=NULL)
			{
				if(nextDevicePtr->GetNextDevice() == simpleDevice)
				{
					nextDevicePtr->SetNextDevice(simpleDevice->GetNextDevice());
					break;
				}
				nextDevicePtr = nextDevicePtr->GetNextDevice();
			}
		}
	}

	device->DeviceClose();
}

pvm_bool PVMDriverSimple::GetDriverOption(const pvm_char* /*optionName*/, void* /*optionVal*/, pvm_uint32* /*optionSize*/)
{
	return false;
}

pvm_bool PVMDriverSimple::SetDriverOption(const pvm_char* /*optionName*/, void* /*optionVal*/, pvm_uint32 /*optionSize*/)
{
	return false;
}


void PVMDriverSimple::OnIdle()
{
	PVMDeviceSimple* nextDevicePtr = rootDevice;
	while(nextDevicePtr!=NULL)
	{
		nextDevicePtr->OnIdle();
		nextDevicePtr = nextDevicePtr->GetNextDevice();
	}

	if(simpleDriver!=NULL)
		simpleDriver->OnIdle();
}
